From b7379e51b481428c723ccc6ab137a7d8b71f6b10 Mon Sep 17 00:00:00 2001 From: Ben Longbons Date: Thu, 10 Jul 2014 17:08:19 -0700 Subject: [PATCH] Add support for external tests --- src/cargo/core/manifest.rs | 10 +++++ src/cargo/util/toml.rs | 72 +++++++++++++++++++++++++++++- tests/test_cargo_test.rs | 90 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 1 deletion(-) diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index 6d130aa6f..fb43b1c43 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -353,6 +353,16 @@ impl Target { } } + pub fn test_target(name: &str, src_path: &Path, profile: &Profile) -> Target { + Target { + kind: BinTarget, + name: name.to_string(), + src_path: src_path.clone(), + profile: profile.clone(), + metadata: None + } + } + pub fn get_name<'a>(&'a self) -> &'a str { self.name.as_slice() } diff --git a/src/cargo/util/toml.rs b/src/cargo/util/toml.rs index b8c62cc07..5a0f35e7f 100644 --- a/src/cargo/util/toml.rs +++ b/src/cargo/util/toml.rs @@ -16,6 +16,7 @@ pub struct Layout { lib: Option, bins: Vec, examples: Vec, + tests: Vec, } impl Layout { @@ -33,6 +34,7 @@ pub fn project_layout(root: &Path) -> Layout { let mut lib = None; let mut bins = vec!(); let mut examples = vec!(); + let mut tests = vec!(); if root.join("src/lib.rs").exists() { lib = Some(root.join("src/lib.rs")); @@ -54,10 +56,22 @@ pub fn project_layout(root: &Path) -> Layout { .map(|mut i| i.collect()) .map(|found| examples.push_all_move(found)); + // support two styles of tests: src/test.rs or tests/*.rs + let _ = fs::readdir(&root.join("tests")) + .map(|v| v.move_iter()) + .map(|i| i.filter(|f| f.extension_str() == Some("rs"))) + .map(|mut i| i.collect()) + .map(|found| tests.push_all_move(found)); + + if root.join("src/test.rs").exists() { + tests.push(root.join("src/test.rs")); + } + Layout { lib: lib, bins: bins, examples: examples, + tests: tests, } } @@ -139,6 +153,7 @@ pub fn parse(toml: &str, file: &str) -> CargoResult { type TomlLibTarget = TomlTarget; type TomlBinTarget = TomlTarget; type TomlExampleTarget = TomlTarget; +type TomlTestTarget = TomlTarget; /* * TODO: Make all struct fields private @@ -168,6 +183,7 @@ pub struct TomlManifest { lib: Option>, bin: Option>, example: Option>, + test: Option>, dependencies: Option>, dev_dependencies: Option> } @@ -254,6 +270,22 @@ fn inferred_example_targets(layout: &Layout) -> Option> { }).collect()) } +fn inferred_test_targets(layout: &Layout) -> Option> { + Some(layout.tests.iter().filter_map(|ex| { + let name = ex.filestem_str().map(|f| f.to_string()); + + name.map(|name| { + TomlTarget { + name: name, + crate_type: None, + path: Some(ex.display().to_string()), + test: None, + plugin: None, + } + }) + }).collect()) +} + impl TomlManifest { pub fn to_manifest(&self, source_id: &SourceId, layout: &Layout) -> CargoResult<(Manifest, Vec)> @@ -319,10 +351,19 @@ impl TomlManifest { }).collect()) }; + let tests = if self.test.is_none() || self.test.get_ref().is_empty() { + inferred_test_targets(layout) + } else { + Some(self.test.get_ref().iter().map(|t| { + t.clone() + }).collect()) + }; + // Get targets let targets = normalize(lib.as_ref().map(|l| l.as_slice()), bins.as_ref().map(|b| b.as_slice()), examples.as_ref().map(|e| e.as_slice()), + tests.as_ref().map(|e| e.as_slice()), &metadata); if targets.is_empty() { @@ -423,10 +464,12 @@ struct TomlTarget { fn normalize(lib: Option<&[TomlLibTarget]>, bin: Option<&[TomlBinTarget]>, example: Option<&[TomlExampleTarget]>, + test: Option<&[TomlTestTarget]>, metadata: &Metadata) -> Vec { - log!(4, "normalizing toml targets; lib={}; bin={}; example={}", lib, bin, example); + log!(4, "normalizing toml targets; lib={}; bin={}; example={}; test={}", + lib, bin, example, test); enum TestDep { Needed, NotNeeded } @@ -495,6 +538,20 @@ fn normalize(lib: Option<&[TomlLibTarget]>, } } + fn test_targets(dst: &mut Vec, tests: &[TomlTestTarget], + default: |&TomlTestTarget| -> String) { + for test in tests.iter() { + let path = test.path.clone().unwrap_or_else(|| default(test)); + + let profile = &Profile::default_test(); + { + dst.push(Target::test_target(test.name.as_slice(), + &Path::new(path.as_slice()), + profile)); + } + } + } + let mut ret = Vec::new(); match (lib, bin) { @@ -522,5 +579,18 @@ fn normalize(lib: Option<&[TomlLibTarget]>, None => () } + match test { + Some(ref tests) => { + test_targets(&mut ret, tests.as_slice(), + |test| { + if test.name.as_slice() == "test" { + "src/test.rs".to_string() + } else { + format!("tests/{}.rs", test.name) + }}); + }, + None => () + } + ret } diff --git a/tests/test_cargo_test.rs b/tests/test_cargo_test.rs index bdc429a4e..ed2136ab5 100644 --- a/tests/test_cargo_test.rs +++ b/tests/test_cargo_test.rs @@ -132,3 +132,93 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\ compiling = COMPILING, dir = p.root().display()).as_slice())); }) + +test!(external_test_explicit { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [[test]] + name = "test" + path = "src/test.rs" + "#) + .file("src/lib.rs", r#" + pub fn get_hello() -> &'static str { "Hello" } + + #[test] + fn internal_test() {} + "#) + .file("src/test.rs", r#" + extern crate foo; + + #[test] + fn external_test() { assert_eq!(foo::get_hello(), "Hello") } + "#); + + let output = p.cargo_process("cargo-test") + .exec_with_output().assert(); + let out = str::from_utf8(output.output.as_slice()).assert(); + + let internal = "\ +running 1 test +test internal_test ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured"; + let external = "\ +running 1 test +test external_test ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured"; + + let head = format!("{compiling} foo v0.0.1 (file:{dir})", + compiling = COMPILING, dir = p.root().display()); + + assert!(out == format!("{}\n\n{}\n\n\n{}\n\n", head, internal, external).as_slice() || + out == format!("{}\n\n{}\n\n\n{}\n\n", head, external, internal).as_slice()); +}) + +test!(external_test_implicit { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", r#" + pub fn get_hello() -> &'static str { "Hello" } + + #[test] + fn internal_test() {} + "#) + .file("src/test.rs", r#" + extern crate foo; + + #[test] + fn external_test() { assert_eq!(foo::get_hello(), "Hello") } + "#); + + let output = p.cargo_process("cargo-test") + .exec_with_output().assert(); + let out = str::from_utf8(output.output.as_slice()).assert(); + + let internal = "\ +running 1 test +test internal_test ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured"; + let external = "\ +running 1 test +test external_test ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured"; + + let head = format!("{compiling} foo v0.0.1 (file:{dir})", + compiling = COMPILING, dir = p.root().display()); + + assert!(out == format!("{}\n\n{}\n\n\n{}\n\n", head, internal, external).as_slice() || + out == format!("{}\n\n{}\n\n\n{}\n\n", head, external, internal).as_slice()); +}) -- 2.30.2